﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;

namespace WebPages.Dal
{
    public class AzureDataSource
    {
        AzureServiceContext serviceContext;

        CloudStorageAccount GetStorageAccount()
        {
            return CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"));
        }

        AzureServiceContext GetServiceContext()
        {
            var storageAccount = GetStorageAccount();
            return new AzureServiceContext(storageAccount.TableEndpoint.ToString(), storageAccount.Credentials);
        }

        void InitServiceContext()
        {
            serviceContext = GetServiceContext();
        }

        void CreateUserTableIfNotExists(CloudStorageAccount storageAccount)
        {
            storageAccount.CreateCloudTableClient().CreateTableIfNotExist(AzureServiceContext.UserTableName);
        }

        void CreateRoleTableIfNotExists(CloudStorageAccount storageAccount)
        {
            storageAccount.CreateCloudTableClient().CreateTableIfNotExist(AzureServiceContext.RoleTableName);
        }

        public AzureDataSource()
        {
            InitServiceContext();

            var storageAccount = GetStorageAccount();
            CreateUserTableIfNotExists(storageAccount);
            CreateRoleTableIfNotExists(storageAccount);
        }

        #region User CRUD operations

        public IEnumerable<UserEntity> GetUsers(string applicationName)
        {
            var results = from c in serviceContext.Users
                          where c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<UserEntity>();
            return query.Execute();
        }

        public IEnumerable<UserEntity> GetUsers(string partitionKey, string applicationName)
        {
            var results = from c in serviceContext.Users
                          where c.PartitionKey == partitionKey && c.RowKey == String.Empty && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<UserEntity>();
            return query.Execute();
        }

        public IEnumerable<UserEntity> FindUsers(string username, string applicationName)
        {
            var results = from c in serviceContext.Users
                          where c.Username.Contains(username) && c.RowKey == String.Empty && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<UserEntity>();
            return query.Execute();
        }

        public IEnumerable<UserEntity> FindInactiveUsers(string username, string applicationName, DateTime userInactiveSinceDate)
        {
            var results = from c in serviceContext.Users
                          where c.Username.Contains(username) && c.RowKey == String.Empty && c.ApplicationName == applicationName && c.LastActivityDate >= userInactiveSinceDate
                          select c;

            var query = results.AsTableServiceQuery<UserEntity>();
            return query.Execute();
        }


        public IEnumerable<UserEntity> GetInactiveUsers(string applicationName, DateTime userInactiveSinceDate)
        {
            var results = from c in serviceContext.Users
                          where c.RowKey == String.Empty && c.ApplicationName == applicationName && c.LastActivityDate >= userInactiveSinceDate
                          select c;

            var query = results.AsTableServiceQuery<UserEntity>();
            return query.Execute();
        }

        /// <summary>
        /// TODO: This method could be very slow with a lot of users, so look at making this faster somehow
        /// </summary>
        /// <param name="email">User e-mail</param>
        /// <returns></returns>
        public IEnumerable<UserEntity> GetUsersByEmail(string email, string applicationName)
        {
            var results = from c in serviceContext.Users
                          where c.Email == email && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<UserEntity>();
            return query.Execute();
        }

        public IEnumerable<UserEntity> GetOnlineUsers(DateTime compareTime, string applicationName)
        {
            var results = from c in serviceContext.Users
                          where c.LastActivityDate > compareTime && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<UserEntity>();
            return query.Execute();
        }

        public void Delete(UserEntity itemToDelete)
        {
            serviceContext.DeleteObject(itemToDelete);
            serviceContext.SaveChanges();
        }

        public void Insert(UserEntity newItem)
        {
            serviceContext.AddObject(AzureServiceContext.UserTableName, newItem);
            serviceContext.SaveChanges();
        }

        public void Update(UserEntity itemToUpdate)
        {
            serviceContext.UpdateObject(itemToUpdate);
            serviceContext.SaveChanges();
        }
    
        #endregion

        #region Role CRUD operations

        public IEnumerable<RoleEntity> GetAllRoles(string applicationName)
        {
            var results = from c in serviceContext.Roles
                          where c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<RoleEntity>();
            return query.Execute();
        }

        public IEnumerable<RoleEntity> GetRolesForUser(string rowKey, string applicationName)
        {
            var results = from c in serviceContext.Roles
                          where c.RowKey == rowKey && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<RoleEntity>();
            return query.Execute();
        }

        public IEnumerable<RoleEntity> GetUsersInRole(string partitionKey, string applicationName)
        {
            var results = from c in serviceContext.Roles
                          where c.PartitionKey == partitionKey && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<RoleEntity>();
            return query.Execute();
        }

        public bool IsUserInRole(string partitionKey, string rowKey, string applicationName)
        {
            var results = from c in serviceContext.Roles
                          where c.PartitionKey == partitionKey && c.RowKey == rowKey && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<RoleEntity>();
            var queryResult = query.Execute();

            return (queryResult.Count() > 0);
        }

        public IEnumerable<RoleEntity> GetUserInRole(string partitionKey, string rowKey, string applicationName)
        {
            var results = from c in serviceContext.Roles
                          where c.PartitionKey == partitionKey && c.RowKey == rowKey && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<RoleEntity>();
            return query.Execute();
        }

        public bool RoleExists(string partitionKey, string applicationName)
        {
            var results = from c in serviceContext.Roles
                          where c.PartitionKey == partitionKey && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<RoleEntity>();
            return query.Execute().Count() > 0;
        }

        public IEnumerable<RoleEntity> FindUsersInRole(string userNameToMatch, string applicationName)
        {
            var results = from c in serviceContext.Roles
                          where c.UserName.Contains(userNameToMatch) && c.ApplicationName == applicationName
                          select c;

            var query = results.AsTableServiceQuery<RoleEntity>();
            return query.Execute();
        }

        public void Delete(RoleEntity itemToDelete)
        {
            serviceContext.DeleteObject(itemToDelete);
            serviceContext.SaveChanges();
        }

        public void Insert(RoleEntity newItem)
        {
            serviceContext.AddObject(AzureServiceContext.RoleTableName, newItem);
            serviceContext.SaveChanges();
        }

        public void Update(RoleEntity itemToUpdate)
        {
            serviceContext.UpdateObject(itemToUpdate);
            serviceContext.SaveChanges();
        }
        #endregion
    }
}